﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EasyCLRBase.AssemblyExplorer;
using Microsoft.SqlServer.Server;
using EasyCLRBase;
using System.Data.SqlTypes;
using System.IO;

namespace EasyCLRMisc
{
    [Export(typeof(EasyCLRBase.IClrBase))]
    [Serializable]
    [SqlUserDefinedType(
        Format.UserDefined,
        Name = "FileInfo",
        IsByteOrdered = true,
        ValidationMethodName = "Validate",
        MaxByteSize = Constants.UserDefinedTypeMaxByte)]
    public struct FileManipulation : IClrBase, IBinarySerialize, INullable, IEquatable<FileManipulation>
    {
        private Boolean _IsNull;
        private String _File;
        private FileInfo _Info;

        public Boolean IsNull
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._IsNull); }
        }
        public String File
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._File); }
            private set
            {
                if (String.IsNullOrEmpty(value) == true)
                    this._IsNull = true;
                
                this._File = value;
                this.Validate();
            }
        }

        #region FileInfo
        public Boolean Exists
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.Exists;
            }
        }
        public Int64 Length
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.Length;
            }
        }
        public String Name
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.Name;
            }
        }
        public String NameWithoutExtension
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return Path.GetFileNameWithoutExtension(this._Info.Name);
            }
        }
        public String FullName
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.FullName;
            }
        }
        public String Extension
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.Extension;
            }
        }
        public String DirectoryName
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.DirectoryName;
            }
        }
        public DateTime CreationTime
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.CreationTime;
            }
        }
        public DateTime CreationTimeUtc
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.CreationTimeUtc;
            }
        }
        public DateTime LastWriteTime
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.LastWriteTime;
            }
        }
        public DateTime LastWriteTimeUtc
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                this.Validate();
                return this._Info.LastWriteTimeUtc;
            }
        }
        #endregion

        public SqlBytes GetBytes
        {
            get
            {
                this.Validate();
                return new SqlBytes(System.IO.File.ReadAllBytes(this._Info.FullName));
            }
        }
        [return: SqlFacet(MaxSize = -1)]
        public SqlString GetContext()
        {
            
            {
                this.Validate();
                return System.IO.File.ReadAllText(this._Info.FullName);
            }
        }

        private Boolean Validate()
        {
            if (String.IsNullOrEmpty(this._File) == true)
                throw new ArgumentNullException("Give file is not valid.");

            if (this._Info == null)
                this._Info = new FileInfo(this._File);
            this._Info.Refresh();
            return true;
        }
        [SqlMethod(OnNullCall = false)]
        public SqlInt64 Prepend(SqlString context)
        {
            this.Validate();
            SqlInt64 result = new SqlInt64();
            if (context.IsNull == false)
            {
                var tmp = new FileInfo(Path.GetTempFileName());
                tmp.Delete();
                System.IO.File.Move(this._Info.FullName, tmp.FullName);
                using (var fs = tmp.Open(FileMode.OpenOrCreate, FileAccess.Read, FileShare.None))
                {
                    fs.Position = 0;
                    using (var ofs = this._Info.Open(FileMode.Create, FileAccess.Write, FileShare.None))
                    {
                        var prepend = Encoding.UTF8.GetBytes(context.Value);
                        ofs.Write(prepend, 0, prepend.Length);
                    #if (NET35)
                        Int64 len = fs.Length;
                        int bsize = 2048;
                        while (len > fs.Position)
                        {
                            var buffer = new Byte[0];
                            if (fs.Position + bsize <= len)
                                buffer = new Byte[bsize];
                            else
                                buffer = new Byte[len - fs.Position];
                            fs.Read(buffer, 0, buffer.Length);
                            ofs.Write(buffer, 0, buffer.Length);
                        }
                    #elif (NET4)
                        fs.CopyTo(ofs);
                        result = ofs.Length;
                    #endif
                    }
                }
            }
            return result;
        }

        [SqlMethod(OnNullCall = false)]
        public SqlInt64 Append(SqlString context)
        {
            System.IO.File.AppendAllText(this.FullName, context.Value);
            return this.Length;
        }

        public SqlInt64 Create(SqlBytes content)
        {
            System.IO.File.WriteAllBytes(this.FullName, content.Value);
            return this.Length;
        }

        public SqlInt64 CreateText(SqlString content)
        {
            System.IO.File.WriteAllText(this.FullName, content.Value);
            return this.Length;
        }

        public SqlBoolean Delete()
        {
            this.Validate();
            this._Info.Delete();
            return this.Exists == false;
        }

        [SqlMethod(OnNullCall = false)]
        public FileManipulation Move(SqlString path)
        {
            this.Validate();
            this._Info.MoveTo(path.Value);
            return FileManipulation.Parse(new FileInfo(path.Value).FullName);
        }

        [SqlMethod(OnNullCall = false)]
        public FileManipulation Copy(SqlString path, SqlBoolean overwrite)
        {
            if (overwrite.IsNull == true)
                overwrite = false;
            this.Validate();
            return FileManipulation.Parse(this._Info.CopyTo(path.Value, overwrite.Value).FullName);
        }

        public static FileManipulation Parse(SqlString str)
        {
            return (str.IsNull == false) ? new FileManipulation() { _File = str.Value, _IsNull = false } : FileManipulation.Null;
        }

        public static FileManipulation Null
        {
            get
            {
                return new FileManipulation() { _IsNull = true };
            }
        }

        #region Interface Override
        public ClrExportString GetClrCreationCode()
        {
            return ClrBase.GetClrCreationCode(this.GetType());
        }
        public void Read(System.IO.BinaryReader r)
        {
            this._IsNull = r.ReadBoolean();
            if (this._IsNull == false)
                this.File = r.ReadString();
        }

        public void Write(System.IO.BinaryWriter w)
        {
            w.Write(this._IsNull);
            if (this.IsNull == false)
            {
                w.Write(this.File);
            }
        }
        public override string ToString()
        {
            return String.IsNullOrEmpty(this._File) ? "NULL" : this._File;
        }
        public override int GetHashCode()
        {
            return HashBuilder.GetHashCode(this._File);
        }
        public override Boolean Equals(object obj)
        {
            return obj is FileManipulation ? this.Equals((FileManipulation)obj) : false;
        }
        public Boolean Equals(FileManipulation other)
        {
            return String.Equals(other._File, this._File);
        }
        public String HowTo()
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
